package org.easyorm.groovy

import groovy.sql.Sql
import org.slf4j.Logger
import org.slf4j.LoggerFactory

public abstract class EntityManager {

	private static final Logger LOGGER = LoggerFactory.getLogger(EntityManager.class)

	private static final Set TYPES_NEED_QUOTATION = ['java.lang.String', 'java.util.Date']

	protected static final Set RESERVED_PROPS = ['metaClass', 'class']

	private Sql sql

	public EntityManager() {}

	public EntityManager(String url, String username, String password, String driverClassName) {
		sql = Sql.newInstance(url, username, password, driverClassName)
	}

	public Collection find(entity) {
		Map props = getFieldValuePairs(entity)
		String stmt = buildFindStmt(getTableName(entity), props)

		LOGGER.debug("SQL Statement: $stmt}")

		def results = []
		sql.eachRow(stmt) {	row ->
			Object obj = entity.getClass().newInstance()
			props.each { key, value ->
				if (!(key in RESERVED_PROPS)) {
					obj."$key" = row."$key"
				}
			}
			results << obj
		}

		return results
	}

	public void create(entity) {
		String sqlStmt = buildInsertStmt(entity)
		LOGGER.debug(sqlStmt)
		sql.execute(sqlStmt)
	}

	public void update(entity) {

	}

	public void delete(entity) {

	}

	protected String buildFindStmt(String tableName, Map properties) {
		StringBuilder stmtBuilder = new StringBuilder("select * from ")
		stmtBuilder << tableName << ' where 1=1 '

		properties.each { key, value ->
			if (value != null) {
				stmtBuilder << ' and ' << key << ' = ' << buildSqlValueString(value)
			}
		}
		return stmtBuilder.toString()
	}

	protected String buildInsertStmt(entity) {
		StringBuilder insertStmtBuilder = new StringBuilder("insert into ")
		insertStmtBuilder << getTableName(entity) << ' ('

		Map fieldValuePairs = getFieldValuePairs(entity)

		fieldValuePairs.keySet().eachWithIndex { field, index ->
			if (fieldValuePairs.get(field) != null) {
				insertStmtBuilder << field
				addCommaIfNecessary(index, fieldValuePairs.size(), insertStmtBuilder)
			}
		}

		insertStmtBuilder << ') values ('

		fieldValuePairs.values().eachWithIndex { value, index ->
			if (value != null) {
				insertStmtBuilder << buildSqlValueString(value)
				addCommaIfNecessary(index, fieldValuePairs.size(), insertStmtBuilder)
			}
		}

		insertStmtBuilder << ')'

		return insertStmtBuilder.toString()
	}

	abstract String getTableName(Object entity)

	abstract Map getFieldValuePairs(Object entity)

	protected void addCommaIfNecessary(int index, int size, StringBuilder stringBuilder) {
		if (index < size - 1) stringBuilder << ', '
	}

	protected String buildSqlValueString(Object value) {
		switch (value.getClass().getName()) {
			case "java.lang.String":
				return "\'$value\'"
			case "java.util.Date":
				String dateString = ((Date) value).format("yyyy-MM-dd hh:mm:ss")
				return "\'$dateString\'"
			default:
				return value.toString()
		}
	}

}
